﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IULibrary
{
    /// <summary>
    /// 异步日志记录器（非阻塞）
    /// 适用于Release版本，自动按日期分割日志文件
    /// 支持日志分级、异常记录、文件大小限制和日期分割
    /// </summary>
    public class IULogger
    {
        /// <summary>
        /// 日志级别枚举
        /// </summary>
        public enum LogLevel
        {
            /// <summary>普通信息</summary>
            Info,
            /// <summary>警告信息</summary>
            Warning,
            /// <summary>错误信息</summary>
            Error,
            /// <summary>致命错误</summary>
            Fatal
        }

        private readonly string _logDirectory;         // 日志存储目录
        private readonly long _maxFileSize;            // 单个日志文件最大字节数（默认10MB）

        // 兼容C# 7.3的异步信号量声明
        private readonly SemaphoreSlim _logSemaphore = new SemaphoreSlim(1, 1);

        /// <summary>
        /// 构造函数（默认10MB文件大小）
        /// </summary>
        /// <param name="logDirectory">日志目录（默认：程序目录/Logs）</param>
        public IULogger(string logDirectory = null)
            : this(10, logDirectory)  // 默认10MB
        {
        }

        /// <summary>
        /// 构造函数（自定义文件大小）
        /// </summary>
        /// <param name="maxFileSizeMB">最大文件大小(MB)</param>
        /// <param name="logDirectory">日志目录</param>
        public IULogger(int maxFileSizeMB, string logDirectory = null)
        {
            // 设置日志目录（默认：程序基目录/Logs）
            _logDirectory = logDirectory ?? Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "Logs");

            // 确保日志目录存在
            if (!Directory.Exists(_logDirectory))
            {
                Directory.CreateDirectory(_logDirectory);
            }

            // 计算字节大小（最小1MB）
            _maxFileSize = Math.Max(maxFileSizeMB, 1) * 1024 * 1024;
        }

        // ================= 公开日志方法 =================
        /// <summary>
        /// 异步记录普通信息
        /// </summary>
        public Task InfoAsync(string message) =>
            WriteLogAsync(LogLevel.Info, message);
        /// <summary>
        /// 异步记录警告
        /// </summary>
        public Task WarningAsync(string message) =>
            WriteLogAsync(LogLevel.Warning, message);
        /// <summary>
        /// 异步记录错误
        /// </summary>
        public Task ErrorAsync(string message, Exception ex = null) =>
            WriteLogAsync(LogLevel.Error, FormatException(message, ex));
        /// <summary>
        /// 异步记录致命错误
        /// </summary>
        public Task FatalAsync(string message, Exception ex = null) =>
            WriteLogAsync(LogLevel.Fatal, FormatException(message, ex));

        /// <summary>
        /// 核心日志写入方法（异步安全）
        /// </summary>
        private async Task WriteLogAsync(LogLevel level, string message)
        {
            // 跳过空消息
            if (string.IsNullOrEmpty(message)) return;

            try
            {
                // 构建日志内容（减少字符串拼接次数）
                var logContent = new StringBuilder(256)
                    .Append('[').Append(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")).Append("] [")
                    .Append(level).Append("] ").AppendLine(message)
                    .AppendLine("------------------------------------------------")
                    .ToString();

                // 使用异步锁确保线程安全
                await _logSemaphore.WaitAsync().ConfigureAwait(false);
                try
                {
                    // 获取当前有效的日志文件路径
                    string logFilePath = GetCurrentLogFilePath();

                    // 兼容C# 7.3的异步写入方式
                    using (var stream = new FileStream(
                        logFilePath,
                        FileMode.Append,
                        FileAccess.Write,
                        FileShare.Read,
                        bufferSize: 4096,
                        useAsync: true))
                    {
                        var contentBytes = Encoding.UTF8.GetBytes(logContent);
                        await stream.WriteAsync(contentBytes, 0, contentBytes.Length).ConfigureAwait(false);
                    }
                }
                finally
                {
                    _logSemaphore.Release();
                }
            }
            catch
            {
                // 忽略所有日志记录异常（保证主流程不受影响）
            }
        }

        /// <summary>
        /// 获取当前有效的日志文件路径
        /// </summary>
        private string GetCurrentLogFilePath()
        {
            // 基础文件名（按日期分割）
            string baseFileName = $"{DateTime.Now:yyyy-MM-dd}.log";
            string basePath = Path.Combine(_logDirectory, baseFileName);

            // 检查文件是否存在及大小
            if (!File.Exists(basePath)) return basePath;

            try
            {
                var fileInfo = new FileInfo(basePath);
                if (fileInfo.Length < _maxFileSize) return basePath;
            }
            catch
            {
                // 文件访问异常时返回基础路径
                return basePath;
            }

            // 生成序号文件（如2023-10-01_1.log）
            string fileNameNoExt = Path.GetFileNameWithoutExtension(baseFileName);
            string extension = Path.GetExtension(baseFileName);

            for (int i = 1; i <= 999; i++)  // 限制最大999个文件
            {
                string newPath = Path.Combine(
                    _logDirectory,
                    $"{fileNameNoExt}_{i}{extension}");

                if (!File.Exists(newPath)) return newPath;

                try
                {
                    var newFileInfo = new FileInfo(newPath);
                    if (newFileInfo.Length < _maxFileSize) return newPath;
                }
                catch
                {
                    // 文件访问异常时尝试下一个序号
                    continue;
                }
            }

            // 超出999时回退到基础文件（避免无限循环）
            return basePath;
        }

        /// <summary>
        /// 格式化异常信息（包含所有内部异常）
        /// </summary>
        private static string FormatException(string message, Exception ex)
        {
            if (ex == null) return message;

            var sb = new StringBuilder(message);
            AppendExceptionDetails(sb, ex, 0);
            return sb.ToString();
        }

        /// <summary>
        /// 递归追加异常详情
        /// </summary>
        private static void AppendExceptionDetails(StringBuilder sb, Exception ex, int depth)
        {
            sb.AppendLine()
              .Append(depth == 0 ? "异常信息：" : $"内部异常 #{depth}：")
              .AppendLine($"类型：{ex.GetType().FullName}")
              .AppendLine($"消息：{ex.Message}")
              .AppendLine($"堆栈跟踪：{ex.StackTrace}");

            if (ex.InnerException != null)
            {
                AppendExceptionDetails(sb, ex.InnerException, depth + 1);
            }
        }
    }
}